﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using IssueVision.Data.Web;
using IssueVision.Common;
using MVVMPlugin;

namespace IssueVision.ViewModel
{
    [ExportPlugin(ViewModelTypes.MyIssuesViewModel, PluginType.ViewModel)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class MyIssuesViewModel : ViewModelBase
    {
        #region "Private Data Members"
        private IIssueVisionModel _issueVisionModel;
        #endregion "Private Data Members"

        #region "Constructor"
        public MyIssuesViewModel()
        {
            _issueVisionModel = PluginCatalogService.Container.GetExportedValue<IIssueVisionModel>();

            // set up event handling
            _issueVisionModel.SaveChangesComplete += _issueVisionModel_SaveChangesComplete;
            _issueVisionModel.GetMyIssuesComplete += _issueVisionModel_GetMyIssuesComplete;
            _issueVisionModel.PropertyChanged += _issueVisionModel_PropertyChanged;

            // load my issues
            _issueVisionModel.GetMyIssuesAsync();
        }
        #endregion "Constructor"

        #region "ICleanup interface implementation"
        public override void Cleanup()
        {
            if (_issueVisionModel != null)
            {
                // unregister all events
                _issueVisionModel.SaveChangesComplete -= _issueVisionModel_SaveChangesComplete;
                _issueVisionModel.GetMyIssuesComplete -= _issueVisionModel_GetMyIssuesComplete;
                _issueVisionModel.PropertyChanged -= _issueVisionModel_PropertyChanged;
                _issueVisionModel = null;
            }
            // set properties back to null
            _myIssues = null;
            CurrentIssue = null;
            // unregister any messages for this ViewModel
            base.Cleanup();
        }
        #endregion "ICleanup interface implementation"

        #region "Public Properties"

        private IEnumerable<Issue> _myIssues;

        private CollectionViewSource _myIssuesSource;

        public CollectionViewSource MyIssuesSource
        {
            get { return _myIssuesSource; }
            private set
            {
                if (!ReferenceEquals(_myIssuesSource, value))
                {
                    _myIssuesSource = value;
                    RaisePropertyChanged("MyIssuesSource");
                }
            }
        }

        private Issue _currentIssue;

        public Issue CurrentIssue
        {
            get { return _currentIssue; }
            private set
            {
                if (!ReferenceEquals(_currentIssue, value))
                {
                    _currentIssue = value;
                    RaisePropertyChanged("CurrentIssue");
                }
            }
        }

        #endregion "Public Properties"

        #region "Public Commands"

        private RelayCommand<SelectionChangedEventArgs> _selectionChangedCommand;

        public RelayCommand<SelectionChangedEventArgs> SelectionChangedCommand
        {
            get
            {
                if (_selectionChangedCommand == null)
                {
                    _selectionChangedCommand = new RelayCommand<SelectionChangedEventArgs>(
                        OnSelectionChangedCommand);
                }
                return _selectionChangedCommand;
            }
        }

        private void OnSelectionChangedCommand(SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 1)
            {
                // cancel any changes before editing another issue
                if (_issueVisionModel.HasChanges)
                {
                    _issueVisionModel.RejectChanges();
                }
                var enumerator = e.AddedItems.GetEnumerator();
                enumerator.MoveNext();
                CurrentIssue = (Issue)enumerator.Current;
                // edit the new selected issue
                AppMessages.EditIssueMessage.Send(CurrentIssue);
            }
        }

        private RelayCommand _refreshCommand;

        public RelayCommand RefreshCommand
        {
            get
            {
                if (_refreshCommand == null)
                {
                    _refreshCommand = new RelayCommand(
                        OnRefreshCommand,
                        () => (_issueVisionModel != null) && !(_issueVisionModel.HasChanges));
                }
                return _refreshCommand;
            }
        }

        private void OnRefreshCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    // re-load my issues
                    _issueVisionModel.GetMyIssuesAsync();
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _submitChangeCommand;

        public RelayCommand SubmitChangeCommand
        {
            get
            {
                if (_submitChangeCommand == null)
                {
                    _submitChangeCommand = new RelayCommand(
                        OnSubmitChangeCommand,
                        () => (_issueVisionModel != null) && (_issueVisionModel.HasChanges));
                }
                return _submitChangeCommand;
            }
        }

        private void OnSubmitChangeCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    if (CurrentIssue != null)
                    {
                        // this should trigger validation even if the Title is not changed and is null
                        if (string.IsNullOrWhiteSpace(CurrentIssue.Title))
                            CurrentIssue.Title = string.Empty;

                        // set ResolutionDate and ResolvedByID based on ResolutionID
                        if (CurrentIssue.ResolutionID == null || CurrentIssue.ResolutionID == 0)
                        {
                            CurrentIssue.ResolutionDate = null;
                            CurrentIssue.ResolvedByID = null;
                        }
                        else
                        {
                            if (CurrentIssue.ResolutionDate == null)
                                CurrentIssue.ResolutionDate = DateTime.Now;
                            if (CurrentIssue.ResolvedByID == null)
                                CurrentIssue.ResolvedByID = WebContext.Current.User.Identity.Name;
                        }

                        if (CurrentIssue.TryValidateObject()
                            && CurrentIssue.TryValidateProperty("IssueID")
                            && CurrentIssue.TryValidateProperty("Title"))
                        {
                            _issueVisionModel.SaveChangesAsync();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _cancelChangeCommand;

        public RelayCommand CancelChangeCommand
        {
            get
            {
                if (_cancelChangeCommand == null)
                {
                    _cancelChangeCommand = new RelayCommand(
                        OnCancelChangeCommand,
                        () => (_issueVisionModel != null) && (_issueVisionModel.HasChanges));
                }
                return _cancelChangeCommand;
            }
        }

        private void OnCancelChangeCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    // ask to confirm canceling the current issue in edit
                    var dialogMessage = new DialogMessage(
                        this,
                        CommonResources.CancelCurrentIssueMessageBoxText,
                        s =>
                        {
                            if (s == MessageBoxResult.OK)
                            {
                                // if confirmed, cancel this issue
                                _issueVisionModel.RejectChanges();
                            }
                        })
                    {
                        Button = MessageBoxButton.OKCancel,
                        Caption = CommonResources.ConfirmMessageBoxCaption
                    };

                    AppMessages.PleaseConfirmMessage.Send(dialogMessage);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        #endregion "Public Commands"

        #region "Private Methods"

        private static void _issueVisionModel_SaveChangesComplete(object sender, SubmitOperationEventArgs e)
        {
            if (e.HasError)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void _issueVisionModel_GetMyIssuesComplete(object sender, EntityResultsArgs<Issue> e)
        {
            if (!e.HasError)
            {
                // cancel any changes before setting MyIssuesSource
                if (_issueVisionModel.HasChanges)
                {
                    _issueVisionModel.RejectChanges();
                }

                _myIssues = e.Results.OrderBy(g => g.StatusID).ThenBy(g => g.Priority);

                MyIssuesSource = new CollectionViewSource {Source = _myIssues};
                MyIssuesSource.View.CurrentChanging += View_CurrentChanging;

                // set the first row as the current issue
                if (_myIssues.Count() >= 1)
                {
                    var enumerator = _myIssues.GetEnumerator();
                    enumerator.MoveNext();
                    CurrentIssue = enumerator.Current;

                    AppMessages.EditIssueMessage.Send(CurrentIssue);
                }
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        private void View_CurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            var theResult = MessageBoxResult.OK;

            if (_issueVisionModel.HasChanges)
            {
                // ask to confirm canceling the current issue in edit
                var dialogMessage = new DialogMessage(
                    this,
                    CommonResources.CancelCurrentIssueMessageBoxText,
                    s => theResult = s)
                {
                    Button = MessageBoxButton.OKCancel,
                    Caption = CommonResources.ConfirmMessageBoxCaption
                };

                AppMessages.PleaseConfirmMessage.Send(dialogMessage);

                if (theResult == MessageBoxResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }

        private void _issueVisionModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("HasChanges"))
            {
                RefreshCommand.RaiseCanExecuteChanged();
                SubmitChangeCommand.RaiseCanExecuteChanged();
                CancelChangeCommand.RaiseCanExecuteChanged();
            }
        }

        #endregion "Private Methods"
    }
}